<template>
  <div class="shape" :class="{ active: active }" @click="selectCurComponent" @mousedown="handleMouseDown"
    @contextmenu="handleContextMenu">
    <div class="shape-point" v-for="(item, index) in (active? pointList : [])" @mousedown="handleMouseDownOnPoint(item)"
      :key="index" :style="getPointStyle(item)">
    </div>
    <slot></slot>
    <div class="transform-bg" v-if="active"></div>
  </div>
</template>

<script>
import { defineComponent, nextTick, ref } from "vue";
import { useStore } from "vuex";

export default defineComponent({
  name: "shape",
  props: {
    // 是否被点击 激活
    active: {
      type: Boolean,
      default: false,
    },
    //
    element: {
      require: true,
      type: Object,
    },
    // 默认样式
    defaultStyle: {
      require: true,
      type: Object,
    },
  },
  emits: ["EmitContextMenu"],
  setup(props, ctx) {
    const store = useStore();
    const pointList = ref(["t", "r", "b", "l", "lt", "rt", "lb", "rb"]);
    const directionKey = ref({
      // 光标显示样式
      t: "n",
      b: "s",
      l: "w",
      r: "e",
    });
    const selectCurComponent = (e) => {
      // 阻止向父组件冒泡
      e.stopPropagation();
      e.preventDefault();
      store.commit("design/selectComp", props.element.id);
    };
    const handleMouseDown = (e) => {
      e.stopPropagation();
      if (!props.element || !props.element.id) return;
      store.commit("design/selectComp", props.element.id);
      const pos = { ...props.element.attr };
      const startY = e.clientY;
      const startX = e.clientX;
      // 如果直接修改属性，值的类型会变为字符串，所以要转为数值型
      const startTop = Number(props.element.attr.y);
      const startLeft = Number(props.element.attr.x);
      // 如果元素没有移动，则不保存快照
      let hasMove = false;
      const move = async (moveEvent) => {
        if (props.element.locked === true) return;
        hasMove = true;
        const currX = moveEvent.clientX;
        const currY = moveEvent.clientY;
        props.element.attr.y = currY - startY + startTop;
        props.element.attr.x = currX - startX + startLeft;
        await nextTick();
        store.commit("design/calcAlignLine", props.element);
        // 修改当前组件样式
        // // 等更新完当前组件的样式并绘制到屏幕后再判断是否需要吸附
        // 触发元素移动事件，用于显示标线、吸附功能
        // 后面两个参数代表鼠标移动方向
        // currY - startY > 0 true 表示向下移动 false 表示向上移动
        // currX - startX > 0 true 表示向右移动 false 表示向左移动
      };

      const up = () => {
        hasMove && store.commit("design/addSnapshot");
        // 触发元素停止移动事件，用于隐藏标线
        store.commit("design/hideAlignLine", props.element);
        document.removeEventListener("mousemove", move);
        document.removeEventListener("mouseup", up);
      };

      document.addEventListener("mousemove", move);
      document.addEventListener("mouseup", up);
    };
    /**
     * 右键菜单
     * @param e
     */
    const handleContextMenu = (e) => {
      console.log(e.target, e.offsetX, e.offsetY);
      ctx.emit("EmitContextMenu", e, props.element);
    };
    const handleMouseDownOnPoint = (point) => {
      const downEvent = window.event;
      downEvent.stopPropagation();
      downEvent.preventDefault();
      const height = Number(props.element.attr.h);
      const width = Number(props.element.attr.w);
      const top = Number(props.element.attr.y);
      const left = Number(props.element.attr.x);
      const startX = downEvent.clientX;
      const startY = downEvent.clientY;
      // 是否需要保存快照
      let needSave = false;
      const move = (moveEvent) => {
        if (props.element.locked === true) return;
        needSave = true;
        const currX = moveEvent.clientX;
        const currY = moveEvent.clientY;
        const disY = currY - startY;
        const disX = currX - startX;
        const hasT = /t/.test(point);
        const hasB = /b/.test(point);
        const hasL = /l/.test(point);
        const hasR = /r/.test(point);
        const newHeight = height + (hasT ? -disY : hasB ? disY : 0);
        const newWidth = width + (hasL ? -disX : hasR ? disX : 0);
        props.element.attr.height = newHeight > 0 ? newHeight : 0;
        props.element.attr.w = newWidth > 0 ? newWidth : 0;
        props.element.attr.x = left + (hasL ? disX : 0);
        props.element.attr.y = top + (hasT ? disY : 0);
      };

      const up = () => {
        document.removeEventListener("mousemove", move);
        document.removeEventListener("mouseup", up);
        // needSave && store.commit("designer/recordSnapshot");
      };

      document.addEventListener("mousemove", move);
      document.addEventListener("mouseup", up);
    };
    const getPointStyle = (point) => {
      const { w, h } = props.element.attr;
      const hasT = /t/.test(point);
      const hasB = /b/.test(point);
      const hasL = /l/.test(point);
      const hasR = /r/.test(point);
      let newLeft = 0;
      let newTop = 0;
      // 四个角的点
      if (point.length === 2) {
        newLeft = hasL ? 0 : w;
        newTop = hasT ? 0 : h;
      } else {
        // 上下两点的点，宽度居中
        if (hasT || hasB) {
          newLeft = w / 2;
          newTop = hasT ? 0 : h;
        }

        // 左右两边的点，高度居中
        if (hasL || hasR) {
          newLeft = hasL ? 0 : w;
          newTop = Math.floor(h / 2);
        }
      }

      const style = {
        marginLeft: hasR ? "-5px" : "-4px",
        marginTop: "-5px",
        left: `${newLeft}px`,
        top: `${newTop}px`,
        cursor:
          point
            .split("")
            .reverse()
            .map((m) => directionKey.value[m])
            .join("") + "-resize",
      };
      return style;
    };
    return {
      pointList,
      directionKey,
      selectCurComponent,
      handleMouseDown,
      handleContextMenu,
      handleMouseDownOnPoint,
      getPointStyle,
    };
  },
});
</script>

<style lang="less" scoped>
.active {
  border: 1px solid #70c0ff;
  user-select: none;
}

.shape {
  position: absolute;
  box-sizing: border-box;
  cursor: move;

  .shape-point {
    position: absolute;
    background: #fff;
    border: 1px solid #59c7f9;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    z-index: 1;
  }

  .transform-bg {
    position: absolute;
    top: 0;
    left: 0;
    z-index: -1;
    width: 100%;
    height: 100%;
    cursor: move;
    background: rgba(0, 231, 255, 0.11);
  }
}
</style>